package org.go.ee.jmx.mbean.scheduler;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.StandardMBean;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;

import org.go.expcetion.SchedulerException;
import org.go.scheduler.Scheduler;
import org.go.trigger.NullSampledStatisticsImpl;
import org.go.trigger.SampledStatistics;

/**
 * 
 * @author hejie
 *
 */
public class SchedulerControl extends StandardMBean implements NotificationEmitter, SchedulerControlMBean {
	/**
	 * @author gkeim
	 */
	private class Emitter extends NotificationBroadcasterSupport {
		/**
		 * @see javax.management.NotificationBroadcasterSupport#getNotificationInfo()
		 */
		@Override
		public MBeanNotificationInfo[] getNotificationInfo() {
			return SchedulerControl.this.getNotificationInfo();
		}
	}

	private static final MBeanNotificationInfo[] NOTIFICATION_INFO;
	private final static SampledStatistics NULL_SAMPLED_STATISTICS = new NullSampledStatisticsImpl();
	static {
		final String[] notifTypes = new String[] { SCHEDULER_STARTED, SCHEDULER_PAUSED, SCHEDULER_SHUTDOWN, };
		final String name = Notification.class.getName();
		final String description = "GoScheduler JMX Event";
		NOTIFICATION_INFO = new MBeanNotificationInfo[] { new MBeanNotificationInfo(notifTypes, name, description), };
	}

	private static Method findMethod(Class targetType, String methodName, Class[] argTypes) throws IntrospectionException {
		BeanInfo beanInfo = Introspector.getBeanInfo(targetType);
		if (beanInfo != null) {
			for (MethodDescriptor methodDesc : beanInfo.getMethodDescriptors()) {
				Method method = methodDesc.getMethod();
				Class[] parameterTypes = method.getParameterTypes();
				if (methodName.equals(method.getName()) && argTypes.length == parameterTypes.length) {
					boolean matchedArgTypes = true;
					for (int i = 0; i < argTypes.length; i++) {
						if (getWrapperIfPrimitive(argTypes[i]) != parameterTypes[i]) {
							matchedArgTypes = false;
							break;
						}
					}
					if (matchedArgTypes) {
						return method;
					}
				}
			}
		}
		return null;
	}

	private static Class getWrapperIfPrimitive(Class c) {
		Class result = c;
		try {
			Field f = c.getField("TYPE");
			f.setAccessible(true);
			result = (Class) f.get(null);
		} catch (Exception e) {
			/**/
		}
		return result;
	}

	@SuppressWarnings("unused")
	private static void invokeSetter(Object target, String attribute, Object value) throws Exception {
		String setterName = "set" + Character.toUpperCase(attribute.charAt(0)) + attribute.substring(1);
		Class[] argTypes = { value.getClass() };
		Method setter = findMethod(target.getClass(), setterName, argTypes);
		if (setter != null) {
			setter.invoke(target, value);
		} else {
			throw new Exception("Unable to find setter for attribute '" + attribute + "' and value '" + value + "'");
		}
	}

	private SampledStatistics sampledStatistics;

	private boolean sampledStatisticsEnabled;

	private final Scheduler scheduler;

	/**
	 * emitter
	 */
	protected final Emitter emitter = new Emitter();

	/**
	 * sequenceNumber
	 */
	protected final AtomicLong sequenceNumber = new AtomicLong();

	/**
	 * SchedulerControl
	 * 
	 * @throws NotCompliantMBeanException
	 */
	protected SchedulerControl(Scheduler scheduler) throws NotCompliantMBeanException {
		super(SchedulerControlMBean.class);
		this.scheduler = scheduler;
		//this.scheduler.addInternalJobListener(this);
		//this.scheduler.addInternalSchedulerListener(this);
		this.sampledStatistics = NULL_SAMPLED_STATISTICS;
		this.sampledStatisticsEnabled = false;
	}

	private Exception newPlainException(Exception e) {
		String type = e.getClass().getName();
		if (type.startsWith("java.") || type.startsWith("javax.")) {
			return e;
		} else {
			Exception result = new Exception(e.getMessage());
			result.setStackTrace(e.getStackTrace());
			return result;
		}
	}

	/**
	 * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener,
	 *      javax.management.NotificationFilter, java.lang.Object)
	 */
	@Override
	public void addNotificationListener(NotificationListener notif, NotificationFilter filter, Object callBack) {
		emitter.addNotificationListener(notif, filter, callBack);
	}

	@Override
	public long getJobsCompletedMostRecentSample() {
		return this.sampledStatistics.getJobsCompletedMostRecentSample();
	}

	@Override
	public long getJobsExecutedMostRecentSample() {
		return this.sampledStatistics.getJobsExecutingMostRecentSample();
	}

	@Override
	public long getJobsScheduledMostRecentSample() {
		return this.sampledStatistics.getJobsScheduledMostRecentSample();
	}

	@Override
	public String getName() {
		return "SchedulerControlMBean.listener";
	}

	/**
	 * @see javax.management.NotificationBroadcaster#getNotificationInfo()
	 */
	@Override
	public MBeanNotificationInfo[] getNotificationInfo() {
		return NOTIFICATION_INFO;
	}

	@Override
	public Map<String, Long> getPerformanceMetrics() {
		Map<String, Long> result = new HashMap<String, Long>();
		result.put("JobsCompleted", Long.valueOf(getJobsCompletedMostRecentSample()));
		result.put("JobsExecuted", Long.valueOf(getJobsExecutedMostRecentSample()));
		result.put("JobsScheduled", Long.valueOf(getJobsScheduledMostRecentSample()));
		return result;
	}

	@Override
	public String getVersion() {
		return scheduler.getVersion();
	}

	@Override
	public synchronized boolean isSampledStatisticsEnabled() {
		return sampledStatisticsEnabled;
	}

	@Override
	public boolean isShutdown() throws SchedulerException {
		return scheduler.isShutdown();
	}

	@Override
	public void pauseAllTriggers() throws Exception {
		try {
			scheduler.pauseAllWorks();
		} catch (Exception e) {
			throw newPlainException(e);
		}
	}

	/**
	 * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
	 */
	@Override
	public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
		emitter.removeNotificationListener(listener);
	}

	/**
	 * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener,
	 *      javax.management.NotificationFilter, java.lang.Object)
	 */
	@Override
	public void removeNotificationListener(NotificationListener notif, NotificationFilter filter, Object callBack) throws ListenerNotFoundException {
		emitter.removeNotificationListener(notif, filter, callBack);
	}

	/**
	 * sendNotification
	 * 
	 * @param eventType
	 */
	public void sendNotification(String eventType) {
		sendNotification(eventType, null, null);
	}

	/**
	 * sendNotification
	 * 
	 * @param eventType
	 * @param data
	 */
	public void sendNotification(String eventType, Object data) {
		sendNotification(eventType, data, null);
	}

	/**
	 * sendNotification
	 * 
	 * @param eventType
	 * @param data
	 * @param msg
	 */
	public void sendNotification(String eventType, Object data, String msg) {
		Notification notif = new Notification(eventType, this, sequenceNumber.incrementAndGet(), System.currentTimeMillis(), msg);
		if (data != null) {
			notif.setUserData(data);
		}
		emitter.sendNotification(notif);
	}

	@Override
	public void shutdown() throws SchedulerException {
		scheduler.shutdownNow();
	}

	@Override
	public void addJob(CompositeData jobDetail, boolean replace) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void addJob(Map<String, Object> abstractJobInfo, boolean replace) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void clear() throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void deleteCalendar(String name) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean deleteJob(String jobName, String jobGroupName) throws Exception {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public TabularData getAllJobDetails() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<CompositeData> getAllTriggers() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<String> getCalendarNames() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public TabularData getCurrentlyExecutingJobs() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public CompositeData getJobDetail(String jobName, String jobGroupName) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<String> getJobGroupNames() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<String> getJobNames(String groupName) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getJobStoreClassName() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Set<String> getPausedTriggerGroups() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getSchedulerInstanceId() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getSchedulerName() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getThreadPoolClassName() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getThreadPoolSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public CompositeData getTrigger(String triggerName, String triggerGroupName) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<String> getTriggerGroupNames() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<String> getTriggerNames(String triggerGroupName) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<CompositeData> getTriggersOfJob(String jobName, String jobGroupName) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getTriggerState(String triggerName, String triggerGroupName) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean interruptJob(String jobName, String jobGroupName) throws Exception {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isStandbyMode() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isStarted() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void pauseJob(String jobName, String groupName) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseJobGroup(String jobGroup) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseJobsContaining(String jobGroupToken) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseJobsEndingWith(String jobGroupSuffix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseJobsStartingWith(String jobGroupPrefix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseTrigger(String triggerName, String triggerGroupName) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseTriggerGroup(String triggerGroup) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseTriggersContaining(String triggerGroupToken) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseTriggersEndingWith(String suffix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void pauseTriggersStartingWith(String triggerGroupPrefix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeAllTriggers() throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeJob(String jobName, String jobGroupName) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeJobGroup(String jobGroup) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeJobsContaining(String jobGroupToken) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeJobsEndingWith(String jobGroupSuffix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeJobsStartingWith(String jobGroupPrefix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeTrigger(String triggerName, String triggerGroupName) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeTriggerGroup(String triggerGroup) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeTriggersContaining(String triggerGroupToken) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeTriggersEndingWith(String triggerGroupSuffix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void resumeTriggersStartingWith(String triggerGroupPrefix) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void scheduleBasicJob(Map<String, Object> jobDetailInfo, Map<String, Object> triggerInfo) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void scheduleJob(Map<String, Object> abstractJobInfo, Map<String, Object> abstractTriggerInfo) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void scheduleJob(String jobName, String jobGroup, Map<String, Object> abstractTriggerInfo) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public Date scheduleJob(String jobName, String jobGroup, String triggerName, String triggerGroup) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setSampledStatisticsEnabled(boolean enabled) {
		// TODO Auto-generated method stub

	}

	@Override
	public void standby() {
		// TODO Auto-generated method stub

	}

	@Override
	public void start() throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public void triggerJob(String jobName, String jobGroupName, Map<String, String> jobDataMap) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean unscheduleJob(String triggerName, String triggerGroup) throws Exception {
		// TODO Auto-generated method stub
		return false;
	}

}
